package core;

import java.util.ArrayList;
import java.util.List;

import persistence.dto.ProductGroupDTO;

import core.Amount;
import core.reports.IVisitor;
import core.reports.TraversalOrder;

/**
 * A ProductGroup is a container that is either contained
 * within another ProductGroup or it is contained in a 
 * StorageUnit.  This class also contains information on 
 * how much of the contained items are needed to make up 
 * a 3 month supply.
 * 
 * @author Spencer Hoffa
 *
 */

public class ProductGroup extends ProductContainer
{
	/** Serializable ID
	 * 
	 */
	private static final long serialVersionUID = -5971256693946897977L;

	/** Defines how much is needed for a 3 Months supply*/
	private Amount threeMonthSupply;
	
	/** The ProductContainer who contains this ProductGroup*/
	private ProductContainer parentContainer;
	
	private boolean updateFlag;
	private boolean removed;
	private boolean newFlag;
	
	/**
	 * crates a new ProductGroupDTO
	 * @return a new product group DTO
	 */
	public ProductGroupDTO createDTO()
	{
		return new ProductGroupDTO(getID(), name, parentContainer.getID(), 
				threeMonthSupply.getSize(),
				threeMonthSupply.getUnit().toString());
	}
	
	/**
	 * Constructor.
	 * @param name The name of this ProductGroup.
	 */
	public ProductGroup(String name, Amount threeMonthSupply, ProductContainer parent) 
			throws IllegalArgumentException
	{
		super();
		assert(name != null && name != "");
		// assert that the name is unique? done by RootStorageUnit?
		assert(threeMonthSupply.getSize()>=0);
		assert(threeMonthSupply.getUnit()!=null);
		assert(parent!=null);
		
		
		if(name == "" || name == null || threeMonthSupply.getSize() < 0 
				|| threeMonthSupply.getUnit() == null || parent == null)
			throw new IllegalArgumentException();
		
		this.name = name;
		this.threeMonthSupply = threeMonthSupply;
		this.groups = new ArrayList<ProductGroup>();
		this.parentContainer = parent;
		this.updateFlag = false;
		this.newFlag = true;
		this.removed = false;
	}
	
	public void setUpdateFlag(boolean val)
	{
		if(val == true)
			updateFlag = true;
		else
			updateFlag = false;
	}
	
	public boolean getUpdateFlag(){return updateFlag;}
	

	/**
	 * Sets the parent of this container. 
	 * 
	 * @param newParent The ProductContainer that contains this ProductGroup.
	 * 
     * @throws NullPointerException if setting failed
	 */
	public void setParentContainer(ProductContainer newParent) throws NullPointerException
	{
		assert(newParent !=null);
		if(newParent==null)
			throw new NullPointerException();
		
		this.parentContainer = newParent;
		
	}
	
	/**
	 * Returns the parentContainer that contains this Product Group.
	 * 
	 * @return The Parent of this ProductGroup.
	 */
	public ProductContainer getParentContainer()
	{
		return parentContainer;
	}
	
	/** 
	 * Set the new Amount needed for a 3 month supply.
	 * 
	 * @param newAmount The object containing the new 3 month
	 * 		supply amount.
         * @return returns true if successful
	 */
	public boolean setThreeMonthSupply(Amount newAmount) throws IllegalArgumentException
	{
		assert(newAmount !=null);
		assert(newAmount.getSize() >=0);
		assert(newAmount.getUnit()!=null);
		
		if(newAmount == null || newAmount.getSize() <0 || newAmount.getUnit() == null)
			throw new IllegalArgumentException();
		
		this.threeMonthSupply = newAmount;
		return true;
	}
	
	/**
	 * Returns the object containing the three month supply data.
	 * 
	 * @return The three month supply Amount object.
	 */
	public Amount getThreeMonthSupplyAmount()
	{
		return threeMonthSupply;
	}

	/**
	 * Returns the type of the container
	 */
	@Override
	public ContainerType getContainerType() 
	{
		return ContainerType.PRODUCT_GROUP;
	}

	/* Question, shouldn't editProductGroup only need one paramter, since the before 
	 * object in this instance is the same productGroup as "this", so we would call 
	 * update by this: prodGroup1.editProductGroup(newProdGroup); not by passing in 
	 * the original item again.  Only the editProductGroup in the manager class would 
	 * need to see the before and after versions of the product group
	 */
	/**
	 * Modifies the product group
	 * @param after the product group to modify
	 * @return the modified productGroup
	 */
	public ProductGroup editProductGroup(ProductGroup after) throws IllegalArgumentException
	{
		assert(after !=null);
		
		if(after == null)
			throw new IllegalArgumentException();
		
		this.setParentContainer(after.getParentContainer());
		this.setName(after.getName());
		this.setThreeMonthSupply(after.getThreeMonthSupplyAmount());
		
		return this;
	}
	
	public void removed()
	{
		removed = true;
	}
	
	public boolean isRemoved()
	{
		return removed;
	}

	@Override
	public void accept(IVisitor v, TraversalOrder order) 
	{
		
		if (order == TraversalOrder.PreOrder)
		{
			v.visitProductGroup(this, order);
			traverseChildren(v, order);
		}
		else if (order == TraversalOrder.PostOrder)
		{
			traverseChildren(v, order);
			v.visitProductGroup(this, order);
		}
		else
		{
			//this should be impossible to get to.
			System.out.println("Error in traversing Tree: Unidentified Order.");
		}
	}
	
	

}
